﻿using System;
using System.Collections.Generic;
using EmbeddedSharp.Core;
using EmbeddedSharp.Core.Function.Arithmetics;
using EmbeddedSharp.Core.Function.Maths;
using EmbeddedSharp.Scripting.Function;
using Zove.Runtime.Core.Expression;

namespace Zove.Runtime.Core.EquationTransformer
{
    public class DefaultEquationTransformer : IEquationTransformer
    {
        protected readonly string expr;

        protected readonly string result;

        protected readonly RuntimeModel rm;

        public ILanguageNature LanguageNature { get; set; }

        /// <summary>
        /// Constructor with language nature
        /// </summary>
        /// <param name="languageNature">language nature</param>
        public DefaultEquationTransformer(ILanguageNature languageNature)
        {
            LanguageNature = languageNature;
        }

        /// <summary>
        /// Constructor with equation and context
        /// </summary>
        /// <param name="equ">equation</param>
        /// <param name="rm">context</param>
        /// <param name="languageNature">language context</param>
        public DefaultEquationTransformer(string equ, RuntimeModel rm, ILanguageNature languageNature)
            : this(languageNature)
        {
            if (equ.IndexOf('=') < 0)
            {
                throw new ArgumentException("equation should have = mark");
            }

            DuplicatedIdentifiers = CheckDuplicated(equ);
            result = equ.Split('=')[0].Trim();
            expr = equ.Split('=')[1].Trim();
            Nodes = new Dictionary<string, Node>();
            this.rm = rm;

            TransformAll();
        }

        private List<string> DuplicatedIdentifiers { get; set; }

        public Dictionary<string, Node> Nodes { get; set; }

        public Node this[string elementName]
        {
            get { return Nodes[elementName]; }
        }

        public Node LeftTree { get; set; }

        public Node RightTree { get; set; }

        #region IEquationTransformer Members

        public Node Transform(string wanted, Node node, Node root)
        {
            FuncNode n = null;
            var finder = new RouteFinder(wanted);

            if (finder.Visit(node))
            {
                while (finder.Route.Count > 0)
                {
                    int direction = finder.Route.Pop();
                    if (node is IdentifierNode
                        ||
                        node is NumberNode)
                    {
                        break;
                    }

                    if (node is OperatorNode)
                    {
                        var operatorNode = (OperatorNode) node;
                        if (direction == 1
                            && (node is DivNode || node is SubNode || node is LogNode))
                        {
                            if (node.GetType().Equals(typeof (DivNode)))
                            {
                                n = new DivNode();
                            }
                            else if (node.GetType().Equals(typeof (SubNode)))
                            {
                                n = new SubNode();
                            }
                            else if (node is LogNode)
                            {
                                n = new LogNode();
                            }
                            var on = n as OperatorNode;
                            if (on == null)
                            {
                                throw new NullReferenceException("The node should be an operator node");
                            }
                            on.Left = operatorNode.Left;
                            on.Right = root;

                            node = operatorNode.Right;
                        }
                        else
                        {
                            var on
                                = (OperatorNode) operatorNode.GetReversedFunction(0);
                            on.Left = root;

                            if (direction == 0)
                            {
                                on.Right
                                    = operatorNode.Right;
                                node = operatorNode.Left;
                            }
                            else
                            {
                                on.Right
                                    = operatorNode.Left;
                                node = operatorNode.Right;
                            }
                            n = on;
                        }
                    }
                    else if (node is FuncNode)
                    {
                        var fn = (FuncNode) node;
                        FuncNode nfn = fn.GetReversedFunction(direction);

                        if (fn.Parameters.Count > 0)
                        {
                            nfn.Parameters.Add(root);
                            for (int index = 0;
                                 index < fn.Parameters.Count;
                                 index++)
                            {
                                if (direction == index)
                                {
                                    node = fn.Parameters[index];
                                    continue;
                                }

                                nfn.Parameters.Add(fn.Parameters[index]);
                            }
                        }
                    }
                    root = n;
                }
            }

            return root;
        }

        #endregion

        public static List<string> CheckDuplicated(string equ)
        {
            var id = new List<string>();
            var dup = new List<string>();
            var lexer = new Lexer(equ);

            while (lexer.HasMoreTokens())
            {
                Token token = lexer.NextToken();
                if (token.TokenType == TokenType.IDENTIFIER)
                {
                    if (id.Contains(token.Value))
                    {
                        dup.Add(token.Value);
                    }
                    else
                    {
                        id.Add(token.Value);
                    }
                }
            }

            return dup;
        }

        public Dictionary<string, string> Transform()
        {
            var exprColl
                = new Dictionary<string, string>();

            foreach (string m in Nodes.Keys)
            {
                Node n = Nodes[m];
                exprColl[m] = LanguageNature.ExpressionTranslator.Translate(n);
            }

            return exprColl;
        }

        /// <summary>
        /// Transforming the expressions
        /// </summary>
        public void TransformAll()
        {
            //exprColl[this.result] = this.expr;
            var p = new ExpressionParser[2];

            p[0] = new ZoomExpressionParser(result, rm)
                       {
                           ConstantManager = null,
                           FunctionManager = LanguageNature.FunctionManager
                       };

            p[1] = new ZoomExpressionParser(expr, rm)
                       {
                           ConstantManager = null,
                           FunctionManager = LanguageNature.FunctionManager
                       };

            var roots = new[] {p[0].Parse(), p[1].Parse()};
            LeftTree = roots[0];
            RightTree = roots[1];

            for (int index = 0; index < 2; index++)
            {
                foreach (Node m in p[index].Variables)
                {
                    if (DuplicatedIdentifiers.Contains(m.Value))
                    {
                        continue;
                    }

                    try
                    {
                        Node node = Transform(
                            m.Value, roots[index], roots[index == 0 ? 1 : 0]);
                        Nodes[m.Value] = node;
                    }
                    catch (NoReversedOperatorException ex)
                    {
                        DefaultDependencyManager.Instance.Logger.WriteLog(ex.Message);
                    }
                    catch (Exception ex)
                    {
                        DefaultDependencyManager.Instance.Logger.WriteLog(ex.Message);
                    }
                }
            }
        }

        public Node Transform(string target, Node root)
        {
            FuncNode newRoot = new UserDefinedFunctionNode();

            return Transform(target, root, newRoot);
        }
    }
}